package in.ac.iitb.cse.nlp.postagger.data;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

public class BiViterbiHMM extends HMM {
	private int prevState[][];
	private Double probMatrix[][];
	boolean debugOn=false/*Config.debug*/;
	private int prevStateS1,prevStateS2;
	private int probMatrixS1,probMatrixS2;

	public BiViterbiHMM()
	{		
		prevState = null;
		probMatrix = null;
		prevStateS1 = prevStateS2 = 0;
		probMatrixS1 = probMatrixS2 = 0;
		
		prevStateS1 = 100;
		prevStateS2 = 100;
		prevState = new int [100+2][100+2];

		probMatrixS1 = 100;
		probMatrixS2 = 100;
		probMatrix = new Double [100+2][100+2];
	}
	
	
	public void init()
	{
		if( tagset == null || sentence == null )
			System.err.println("BiViterbiHMM Clue 2");

		checkSentence(); // Check Sentence has ^ and $
		
		nTags = tagset.size();
		nWords = sentence.size();
		
		for(int i=0;i<nWords;i++)
			bestTagSeq.add("UNK");
		
		if( prevState ==null || nTags > prevStateS1 || nWords > prevStateS2 )
		{
			prevStateS1 = nTags;
			prevStateS2 = nWords;
			prevState = new int [nTags+2][nWords+2];
		}
		if( probMatrix ==null || nTags > probMatrixS1 || nWords > probMatrixS2 )
		{
			probMatrixS1 = nTags;
			probMatrixS2 = nWords;
			probMatrix = new Double [nTags+2][nWords+2];
		}
		
		for( int i=0; i< nTags+2; i++)
			for( int j=0; j< nWords+2; j++)
			{
					prevState[i][j] = 0;
					probMatrix[i][j] = 0.0;
			}
		
		nWords = nWords - 1; // ?? Dont consider state comes after Doller $
	}
	
	public List<String> bestProbTagSeq( List<String> Sentence)
	{
		bestTagSeq = new ArrayList<String>();
		sentence = Sentence;
		init();
		forwardBestProbTagSeq(0,nWords-1);
		/*
		System.out.println(bestTagSeq.toString());
		int unknownIndex = -1;
		for(int i=0;i<nWords;i++)
			if( bestTagSeq.get(i).equalsIgnoreCase("UNK") == true){
				unknownIndex = i; break;
			}
		if(unknownIndex != -1)
			backwardBestProbTagSeq(unknownIndex, nWords-1);
		System.out.println(bestTagSeq.toString());		
		*/	
		makeSeq();
		return bestTagSeq;
	}
	
	public int forwardBestProbTagSeq(int start , int  end)
	{
		Double transProb,tProb,eProb;
		Double prob;
		int stateIndex=0;
						
		probMatrix[tagset.indexOf("^")][start] = 1.0; 
		for( stateIndex=start; stateIndex<=end; stateIndex++)
		{
			if ( preProcessInputWord(stateIndex, sentence) !=0 )
				continue;			
				
			///// Tagi belongs to state(stateIndex)
			for(int tagi=0; tagi < nTags; tagi++) 
			{
//				eProb = em.getProbability(tagset.get(tagi),sentence.get(stateIndex));													
//				if(probMatrix[tagi][stateIndex] != 0.0 && eProb != 0.0)
				if(probMatrix[tagi][stateIndex] != 0.0)
				{
					///// Tagj belongs to state(stateIndex+1)
					for(int tagj=0; tagj < nTags; tagj++)  
					{
						eProb = em.getProbability(tagset.get(tagj),sentence.get(stateIndex+1));
						/////		  WORD(stateIndex)
						///// Tagi --------------------> Tagj 
						tProb = tm.getProbability(tagset.get(tagi), tagset.get(tagj));
						if( tProb !=0.0 )
						{
							transProb = tProb * eProb;						
							///// S(ZERO) -> S(stateIndex-1) * transProb(stateIndex ---------> stateIndex+1 )
							prob = transProb * probMatrix[tagi][stateIndex] ;						
							prob = prob * 1; // SCALE						
							if( prob > probMatrix[tagj][stateIndex+1] )
							{
								probMatrix[tagj][stateIndex+1] = prob;
								prevState[tagj][stateIndex+1] = tagi;
							}
						}//if( tProb !=0.0 )
					}//for(int tagj=0; tagj < nTags; tagj++)					
				}//if(probMatrix[tagi][stateIndex] != 0.0)
			}//for(int tagi=0; tagi < nTags; tagi++)
			
			if(debugOn)
			Dump(sentence, tagset);
		}
		constructBackwardBestTagSeq(bestTagSeq, tagset);
		return 0;
	}
	
	public int backwardBestProbTagSeq(int end , int  start)
	{
		Double transProb,tProb,eProb;
		Double prob;
		int stateIndex=0;
		//Inclusive of start to end
		// ^(0) A(1) Boy(2) came(3) $(4) ... start=5-2... 3++-> 4..
		// Will do this from 4 to 1... for each loop previous state will get updated...
		start++;
		probMatrix[tagset.indexOf("$")][start] = 1.0; 
		for( stateIndex=start; stateIndex>end; stateIndex--)
		{
			if ( preProcessInputWord(stateIndex, sentence) !=0 )
				continue;			
				
			///// Tagi belongs to state(stateIndex)
			for(int tagi=0; tagi < nTags; tagi++) 
			{
				if(probMatrix[tagi][stateIndex] != 0.0)
				{
					///// Tagj belongs to state(stateIndex+1)
					for(int tagj=0; tagj < nTags; tagj++)  
					{
						/////		  WORD(stateIndex-1)
						///// Tagj --------------------> Tagi 
						tProb = tm.getProbability(tagset.get(tagj), tagset.get(tagi));
						if( tProb !=0.0 )
						{
							eProb = em.getProbability(tagset.get(tagj),sentence.get(stateIndex-1));													
							if( eProb !=0.0 )
							{
								transProb = tProb * eProb;						
								///// S(ZERO) -> S(stateIndex-2) * transProb(stateIndex-1 ---------> stateIndex )
								prob = transProb * probMatrix[tagi][stateIndex] ;						
								prob = prob * 1; // SCALE						
								if( prob > probMatrix[tagj][stateIndex-1] )
								{
									probMatrix[tagj][stateIndex-1] = prob;
									prevState[tagj][stateIndex-1] = tagi;
								}
							}
						}
					}					
				}
			}
			if(debugOn)
			Dump(sentence, tagset);
		}
		constructForwardBestTagSeq(bestTagSeq, tagset, start, end);
		return 0;
	}
	
	public int preProcessInputWord(int index, List<String> Sentence)
	{
		return 0;
	}
	
	public void constructBackwardBestTagSeq(List <String> bestTagSeq, List<String> Tagset)
	{
		int maxProbIndex = 0;
		int lastindex = nWords;
		Double maxProb = probMatrix[0][lastindex];
		
		do {
			for(int index = 1; index < nTags; index++)
			if(maxProb < probMatrix[index][lastindex]){
				maxProb = probMatrix[index][lastindex];
				maxProbIndex = index;
			}
			if(maxProb == 0.0){
				bestTagSeq.set(lastindex, "UNK");
				lastindex --;
			}else{
				bestTagSeq.set(lastindex, Tagset.get(maxProbIndex));
				break;
			}
		} while(maxProb == 0.0 && lastindex >0);
				
		
		for(int state = lastindex; state > 0; state-- )
		{
			maxProbIndex = prevState[maxProbIndex][state];
			bestTagSeq.set(state-1, Tagset.get(maxProbIndex));			
		}
		
		//TODO
		if(bestTagSeq.size()>0) bestTagSeq.remove(bestTagSeq.size()-1);
	}
	
	public void constructForwardBestTagSeq(List <String> bestTagSeq, List<String> Tagset, int end, int start)
	{
		int maxProbIndex = 0;
		Double maxProb = probMatrix[0][start];
		do {
		
			for(int index = 1; index < nTags; index++)
			if(maxProb < probMatrix[index][start]){
				maxProb = probMatrix[index][start];
				maxProbIndex = index;
			}
			if(maxProb == 0.0){
				//bestTagSeq.set(start, "UNK"); // leave whatever is there already
				start ++;
			}else{
				bestTagSeq.set(start, Tagset.get(maxProbIndex));
				break;
			}
		} while(maxProb == 0.0 && start <= end);
				
		
		for(int state = start; state < end ; state++ )
		{
			maxProbIndex = prevState[maxProbIndex][state];
			bestTagSeq.set(state+1, Tagset.get(maxProbIndex));			
		}
	}	
	
	public void Dump(List <String> Sentence, List<String> Tagset)
	{
		try
		{
			File file = new File("/tmp/0000_nlp_prob_mat.csv");
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(file)));
			
			writer.write("WORD,");

			for(int j=0; j<= nWords; j++)
			{
				writer.write(Sentence.get(j)+",");
			}
			writer.newLine();
			for(int i=0; i< nTags; i++)
			{
				writer.write(Tagset.get(i)+",");
				for(int j=0; j< nWords+1; j++)
				{
					writer.write(probMatrix[i][j]+"["+prevState[i][j]+"],");
				}
				writer.write("END");
				writer.newLine();
			}
			writer.close();
		} catch(IOException e)
		{
			System.out.println(e);
		}
	}
	
	public boolean isTheCurrentStateOK(int state, List<String> Sentence, List<String> Tagset, TransitionMatrix tm, EmissionMatrix em)
	{
		int i,j;
		boolean isWorldAppeared = false;
		List<String> wordTagSet = new ArrayList<String>();
		List<String> tagTagSet = new ArrayList<String>();
		
		if(state < 1 ) return true;
		
		String word = Sentence.get(state-1);
		
		
		for(i=0; i<nTags; i++)
			if( 0.0 != probMatrix[i][state] )
				return true;

		//System.out.println(state + " " +(state-1)+" "+(state-2));
		
		// Is the alien world
		// Checking the Emission Table for the possibility of ZERO Values		
		for(i=0; i<nTags; i++)
			if( 0.0 != em.getProbability( Tagset.get(i) , word) )
			{
				isWorldAppeared = true;
				wordTagSet.add(Tagset.get(i));
			}
		if( false == isWorldAppeared )
		{
			System.out.println("I have never seen a word [[["+word+"]]].");
			return false;
		}
		
		if(state < 2 ) return false; //Can't find the reason possibly coding error
		
		//System.out.println(Sentence.get(state) + " " +Sentence.get(state-1)+" "+Sentence.get(state-2));

		
		// Checking the Transition Table for the possibility of ZERO Tag1 -> Tag2 combinations
		String pword = Sentence.get(state-2);
		
		for(i=0;i<nTags;i++)
			if(0.0 != probMatrix[i][state-1])
				tagTagSet.add(Tagset.get(i));
		
		if(0 == tagTagSet.size()) return false; //Can't find the reason

		for(i=0;i<tagTagSet.size();i++)
			for(j=0;j<wordTagSet.size();j++)
				if( true == tagTagSet.get(i).equals(wordTagSet.get(j)) && 0.0 != probMatrix[ Tagset.indexOf(tagTagSet.get(i)) ][state-1] )
				{
					System.out.println("The level with word [["+pword+"]] has the {"+ tagTagSet.toString() +"} non-zero probable tags/states");
					System.out.println("The next level with word [["+word+"]] tagged with {"+ wordTagSet.toString() +"} tags");
					System.out.println("On Emitting [["+word+"]] ,probMatrix["+ Tagset.indexOf(tagTagSet.get(i)) + "]["+(state-1)+"]=" + probMatrix[ Tagset.indexOf(tagTagSet.get(i)) ][state-1] +" has nonzero value for the column in ZERO");					
					return true; // true or false ?
				}
		
		System.out.println("The level with word [["+pword+"]] has the {"+ tagTagSet.toString() +"} non-zero probable tags/states");
		System.out.println("The next level with word [["+word+"]] tagged with {"+ wordTagSet.toString() +"} tags");
		System.out.println("All the possibilities two set of tags (P(tag1--(WORD)-->tag2)) are Zero");
		
		return false;
	}
	@Override

	public Double ProbTagSeq(List<String> Tagseq, List<String> Sentence)
	{
		Double transProb,tProb,eProb,FinalProp;
		int stateIndex=0;
		boolean isPrint = true;

		FinalProp = 1.0;
								
		for( stateIndex=0; stateIndex < Tagseq.size()-1; stateIndex++) 
		{
			
			System.out.print("P("+Tagseq.get(stateIndex)+"--"+Sentence.get(stateIndex)+"-->"+Tagseq.get(stateIndex+1)+") = ");

			eProb = em.getProbability(Tagseq.get(stateIndex),Sentence.get(stateIndex)); //Ulta
			if( true == isPrint)
			System.out.print("P("+Sentence.get(stateIndex)+"/"+Tagseq.get(stateIndex)+") * ");

			tProb = tm.getProbability(Tagseq.get(stateIndex), Tagseq.get(stateIndex+1)); //Ulta
			if( true == isPrint)
			System.out.print("P("+Tagseq.get(stateIndex+1)+"/"+Tagseq.get(stateIndex) +") = ");
			
			transProb = tProb * eProb;
			if( true == isPrint)
			System.out.print(" "+eProb + " * "+ tProb +" = "+  transProb);
			
			FinalProp = FinalProp * transProb ;
			
			//if( true == isPrint)
			///System.out.print(" ["+FinalProp+"]");
			
			if( true == isPrint)
			System.out.print("\n");			
		
		}				
		
		return FinalProp;
	}



	@Override
	public Tag handleUnknownWord(int index) {
		Tag t = DataMaps.tags.get("VVB");
		return t;
	}
}

